package org.infoset.xml.sax;

import java.io.*;
import java.net.*;

import org.xml.sax.*;
import javax.xml.parsers.*;

import org.infoset.xml.*;
import org.infoset.xml.util.*;

public class SAXDocumentLoader implements DocumentLoader {

   public interface XMLReaderInstantiator {
      XMLReader newInstance()
         throws SAXException,ParserConfigurationException;
   }
   
   static class PFactoryXMLReaderFactory implements XMLReaderInstantiator {
      SAXParserFactory parserFactory;
      PFactoryXMLReaderFactory(SAXParserFactory parserFactory) {
         this.parserFactory = parserFactory;
      }
      
      public XMLReader newInstance() 
         throws SAXException,ParserConfigurationException
      {
         SAXParser parser = parserFactory.newSAXParser();
         return parser.getXMLReader();
      }
   }
   
   XMLReaderInstantiator factory;
   Infoset infoset;

   public SAXDocumentLoader() 
   {
      SAXParserFactory pfactory = SAXParserFactory.newInstance();
      factory = new PFactoryXMLReaderFactory(pfactory);
      infoset = InfosetFactory.getDefaultInfoset();
   }

   public SAXDocumentLoader(SAXParserFactory pfactory) 
   {
      this(new PFactoryXMLReaderFactory(pfactory));
   }
   
   public SAXDocumentLoader(XMLReaderInstantiator factory) {
      this.factory = factory;
      infoset = InfosetFactory.getDefaultInfoset();
   }

   /*
   public SAXDocumentLoader(XMLReader reader) 
      throws SAXException
   { 
      this.reader = reader;
      reader.setFeature("http://xml.org/sax/features/namespaces", true);
      constructor = InfosetFactory.getDefaultInfoset().createItemConstructor();
   }*/

   public void setInfoset(Infoset infoset) {
      this.infoset = infoset;
   }

   public Document load(Reader input) 
      throws java.io.IOException,XMLException
   {
      DocumentDestination docBuilder = new DocumentDestination();
      generate(input,docBuilder);
      return docBuilder.getDocument();
   }
   
   public Document load(Reader input,URI baseURI) 
      throws java.io.IOException,XMLException
   {
      DocumentDestination docBuilder = new DocumentDestination();
      generate(input,baseURI,docBuilder);
      return docBuilder.getDocument();
   }
   
   public Document load(URI uri) 
      throws java.io.IOException,XMLException
   {
      DocumentDestination docBuilder = new DocumentDestination();
      generate(uri,docBuilder);
      return docBuilder.getDocument();
   }
   
   public void generate(URI uri, ItemDestination dest) 
      throws java.io.IOException,XMLException
   {
      // TODO: allow encoding to be controlled
      Reader r = new BufferedReader(new InputStreamReader(uri.toURL().openStream(),"UTF8"));
      InputSource is = new InputSource(r);
      is.setSystemId(uri.toString());
      generate(is,dest);
      r.close();
   }
   
   public void generate(Reader source,URI baseURI,ItemDestination dest)
      throws java.io.IOException,XMLException
   {
      InputSource is = new InputSource(source);
      if (baseURI!=null) {
         is.setSystemId(baseURI.toString());
      }
      generate(is,dest);
   }

   public void generate(Reader source,ItemDestination dest)
      throws java.io.IOException,XMLException
   {
      generate(new InputSource(source),dest);
   }

   public void generate(InputSource source,ItemDestination dest)
      throws java.io.IOException,XMLException
   {
      try {
         XMLReader reader = factory.newInstance();
         reader.setFeature("http://xml.org/sax/features/namespaces", true);
         SAXEventGenerator generator = new SAXEventGenerator();
         String id = source.getSystemId();
         ItemConstructor constructor = null;
         if (id!=null) {
            //System.out.println("System id: "+id);
            URI base = new URI(id);
            generator.setForcedBaseURI(base);
            constructor = infoset.createItemConstructor(base);
         } else {
            constructor = infoset.createItemConstructor();
         }
         generator.setItemConstructor(constructor);
         generator.setItemDestination(dest);
         reader.setContentHandler(generator);
         reader.setProperty("http://xml.org/sax/properties/lexical-handler",generator);
         reader.parse(source);
       } catch (SAXParseException ex) {
         /*
         if (ex.getSystemId()==null) {
            //TODO: sometimes the parser doesn't set the main system id, is this ok?
            ex = new SAXParseException(ex.getMessage(),ex.getPublicId(),source.getSystemId(),ex.getLineNumber(),ex.getColumnNumber());
         }*/
         if (ex.getException()==null) {
            throw new XMLException(ex.getMessage());
         } else {
            throw new XMLException(ex.getMessage(),ex.getException());
         }
      } catch (SAXException ex) {
         Exception cause = ex.getException();
         if (cause==null) {
            throw new XMLException(ex.getMessage(),ex);
         } else {
            if (cause instanceof XMLException) {
               throw (XMLException)cause;
            } else {
               throw new XMLException(ex.getMessage(),cause);
            }
         }
      } catch (ParserConfigurationException ex) {
         throw new XMLException("SAX configuration error: "+ex.getMessage(),ex);
      } catch (URISyntaxException ex) {
         throw new XMLException("Base base URI: "+ex.getMessage(),ex);
      }
   }

}
